﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Data.Entity;
using System.Linq;
using System.Web.Security;
using Tiscs.Practices.Diagnostics;
using Tiscs.Practices.Web.Providers.Entities;

namespace Tiscs.Practices.Web.Providers
{
    /// <summary>
    /// 角色提供程序。
    /// </summary>
    public abstract class DefaultRoleProvider : RoleProvider
    {
        #region Abstract Methods

        /// <summary>
        /// 创建DbContext实例。
        /// </summary>
        /// <returns>DbContext实例。</returns>
        protected abstract DbContext CreateDbContext();

        #endregion

        #region RoleProvider

        /// <summary>
        /// 应用程序名称。
        /// </summary>
        public override string ApplicationName { get; set; }

        /// <summary>
        /// 初始化提供程序。
        /// </summary>
        /// <param name="name">会话状态提供程序的友好名称。</param>
        /// <param name="config">会话状态提供程序初始化配置。</param>
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (string.IsNullOrEmpty(name))
            {
                name = "DefaultRoleProvider";
            }
            base.Initialize(name, config);
            if (string.IsNullOrEmpty(config["applicationName"]))
            {
                this.ApplicationName = ProvidersHelper.GetDefaultApplicationName();
            }
            else
            {
                this.ApplicationName = config["applicationName"];
            }
            config.Remove("applicationName");
            if (config.Count > 0)
            {
                throw new ProviderException(string.Format(ProvidersHelper.Unrecognized_Attribute, config.GetKey(0)));
            }
        }

        /// <summary>
        /// 将指定的用户添加到指定的角色中。
        /// </summary>
        /// <param name="userNames">用户名数组。</param>
        /// <param name="roleNames">角色名数组。</param>
        public override void AddUsersToRoles(string[] userNames, string[] roleNames)
        {
            if (userNames == null)
            {
                throw new ArgumentNullException("userNames");
            }
            if (roleNames == null)
            {
                throw new ArgumentNullException("roleNames");
            }
            using (DbContext db = CreateDbContext())
            {
                List<UserEntity> users = new List<UserEntity>();
                foreach (string userName in userNames)
                {
                    UserEntity user = db.GetUser(ApplicationName, userName.Trim());
                    if (user == null)
                    {
                        throw new ProviderException(string.Format(ProvidersHelper.User_Not_Found, userName));
                    }
                    users.Add(user);
                }
                List<RoleEntity> roles = new List<RoleEntity>();
                foreach (string roleName in roleNames)
                {
                    RoleEntity role = db.GetRole(ApplicationName, roleName.Trim());
                    if (role == null)
                    {
                        throw new ProviderException(string.Format(ProvidersHelper.Role_Not_Found, roleName));
                    }
                    roles.Add(role);
                }
                foreach (UserEntity user in users)
                {
                    foreach (RoleEntity role in roles)
                    {
                        if (user.Roles.Any(r => r.RoleId == role.RoleId))
                        {
                            throw new ProviderException(string.Format(ProvidersHelper.User_Already_In_Role, user.UserName, role.RoleName));
                        }
                        user.Roles.Add(role);
                    }
                }
                db.SaveChanges();
            }
        }

        /// <summary>
        /// 创建角色。
        /// </summary>
        /// <param name="roleName">角色名。</param>
        public override void CreateRole(string roleName)
        {
            Exception exception = Validation.Parameter<string>("roleName").AssertNotNullOrEmpty().AssertMaxLength(RoleEntity.RoleNameMaxLength).AssertNoCommas().TrimAndValidate(ref roleName);
            if (exception != null)
            {
                throw exception;
            }
            using (DbContext db = CreateDbContext())
            {
                ApplicationEntity application = db.GetApplication(ApplicationName, true);
                RoleEntity role = db.GetRole(ApplicationName, roleName);
                if (role != null)
                {
                    throw new ProviderException(string.Format(ProvidersHelper.Role_Already_Exists, roleName));
                }
                role = new RoleEntity()
                {
                    ApplicationId = application.ApplicationId,
                    RoleId = Guid.NewGuid(),
                    RoleName = roleName
                };
                db.GetRoles().Add(role);
                db.SaveChanges();
            }
        }

        /// <summary>
        /// 删除角色。
        /// </summary>
        /// <param name="roleName">角色名。</param>
        /// <param name="throwOnPopulatedRole">角色下用户不为空时是否抛出异常。</param>
        /// <returns>是否删除成功。</returns>
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            Exception exception = Validation.Parameter<string>("roleName").AssertNotNullOrEmpty().AssertMaxLength(RoleEntity.RoleNameMaxLength).AssertNoCommas().TrimAndValidate(ref roleName);
            if (exception != null)
            {
                throw exception;
            }
            bool result;
            using (DbContext db = CreateDbContext())
            {
                RoleEntity role = db.GetRole(ApplicationName, roleName);
                if (role == null)
                {
                    result = false;
                }
                else
                {
                    if (throwOnPopulatedRole && role.Users.Count > 0)
                    {
                        throw new ProviderException(ProvidersHelper.Role_Is_Not_Empty);
                    }
                    role.Users.Clear();
                    db.GetRoles().Remove(role);
                    db.SaveChanges();
                    result = true;
                }
                return result;
            }
        }

        /// <summary>
        /// 查找角色下匹配查找用户。
        /// </summary>
        /// <param name="roleName">角色名。</param>
        /// <param name="userNameToMatch">用于匹配查询的用户名。</param>
        /// <returns>查找到的用户名数组。</returns>
        public override string[] FindUsersInRole(string roleName, string userNameToMatch)
        {
            Exception exception = Validation.Parameter<string>("userNameToMatch").AssertNotNullOrEmpty().AssertMaxLength(UserEntity.UserNameMaxLength).TrimAndValidate(ref userNameToMatch);
            if (exception != null)
            {
                throw exception;
            }
            exception = Validation.Parameter<string>("roleName").AssertNotNullOrEmpty().AssertMaxLength(RoleEntity.RoleNameMaxLength).AssertNoCommas().TrimAndValidate(ref roleName);
            if (exception != null)
            {
                throw exception;
            }
            string[] result;
            using (DbContext db = CreateDbContext())
            {
                RoleEntity role = db.GetRole(ApplicationName, roleName);
                if (role == null)
                {
                    throw new ProviderException(string.Format(ProvidersHelper.Role_Not_Found, roleName));
                }
                result = role.Users.Where(u => u.UserName.ToLower().Contains(userNameToMatch.ToLower())).Select(u => u.UserName).ToArray();
            }
            return result;
        }

        /// <summary>
        /// 获取所有角色。
        /// </summary>
        /// <returns>角色名称数组。</returns>
        public override string[] GetAllRoles()
        {
            string[] result;
            using (DbContext db = CreateDbContext())
            {
                IQueryable<RoleEntity> roles = db.GetRoles(ApplicationName);
                result = roles.Select(r => r.RoleName).ToArray();
            }
            return result;
        }

        /// <summary>
        /// 获取指定用户的所有角色。
        /// </summary>
        /// <param name="userName">用户名。</param>
        /// <returns>查找到的角色名数组。</returns>
        public override string[] GetRolesForUser(string userName)
        {
            Exception exception = Validation.Parameter<string>("userName").AssertNotNull().AssertMaxLength(UserEntity.UserNameMaxLength).AssertNoCommas().TrimAndValidate(ref userName);
            if (exception != null)
            {
                throw exception;
            }
            if (userName.Length == 0)
            {
                return new string[0];
            }
            string[] roles;
            using (DbContext db = CreateDbContext())
            {
                UserEntity user = db.GetUser(ApplicationName, userName);
                if (user == null)
                {
                    roles = new string[0];
                }
                else
                {
                    roles = user.Roles.Select(r => r.RoleName).ToArray();
                }
            }
            return roles;
        }

        /// <summary>
        /// 获取指定角色下的所有用户。
        /// </summary>
        /// <param name="roleName">角色名。</param>
        /// <returns>查找到的用户名数组。</returns>
        public override string[] GetUsersInRole(string roleName)
        {
            Exception exception = Validation.Parameter<string>("roleName").AssertNotNullOrEmpty().AssertMaxLength(RoleEntity.RoleNameMaxLength).AssertNoCommas().TrimAndValidate(ref roleName);
            if (exception != null)
            {
                throw exception;
            }
            string[] users;
            using (DbContext db = CreateDbContext())
            {
                RoleEntity role = db.GetRole(ApplicationName, roleName);
                if (role == null)
                {
                    users = new string[0];
                }
                else
                {
                    users = role.Users.Select(u => u.UserName).ToArray();
                }
            }
            return users;
        }

        /// <summary>
        /// 判断角色中是否存在指定的用户。
        /// </summary>
        /// <param name="userName">用户名。</param>
        /// <param name="roleName">角色名。</param>
        /// <returns>指示角色中是否存在指定用户的布尔值。</returns>
        public override bool IsUserInRole(string userName, string roleName)
        {
            Exception exception = Validation.Parameter<string>("userName").AssertNotNull().AssertMaxLength(UserEntity.UserNameMaxLength).AssertNoCommas().TrimAndValidate(ref userName);
            if (exception != null)
            {
                throw exception;
            }
            exception = Validation.Parameter<string>("roleName").AssertNotNullOrEmpty().AssertMaxLength(RoleEntity.RoleNameMaxLength).AssertNoCommas().TrimAndValidate(ref roleName);
            if (exception != null)
            {
                throw exception;
            }
            if (userName.Length == 0)
            {
                return false;
            }
            bool result;
            using (DbContext db = CreateDbContext())
            {
                RoleEntity role = db.GetRole(ApplicationName, roleName);
                if (role == null)
                {
                    result = false;
                }
                else
                {
                    result = role.Users.Any(u => u.UserName.ToLower() == userName.ToLower());
                }
            }
            return result;
        }

        /// <summary>
        /// 将指定的用户从指定角色中移除。
        /// </summary>
        /// <param name="userNames">用户名数组。</param>
        /// <param name="roleNames">角色名数组。</param>
        public override void RemoveUsersFromRoles(string[] userNames, string[] roleNames)
        {
            if (userNames == null)
            {
                throw new ArgumentNullException("userNames");
            }
            if (roleNames == null)
            {
                throw new ArgumentNullException("roleNames");
            }
            using (DbContext db = CreateDbContext())
            {
                List<UserEntity> users = new List<UserEntity>();
                foreach (string userName in userNames)
                {
                    UserEntity user = db.GetUser(ApplicationName, userName.Trim());
                    if (user == null)
                    {
                        throw new ProviderException(string.Format(ProvidersHelper.User_Not_Found, userName));
                    }
                    users.Add(user);
                }
                List<RoleEntity> roles = new List<RoleEntity>();
                foreach (string roleName in roleNames)
                {
                    RoleEntity role = db.GetRole(ApplicationName, roleName.Trim());
                    if (role == null)
                    {
                        throw new ProviderException(string.Format(ProvidersHelper.Role_Not_Found, roleName));
                    }
                    roles.Add(role);
                }
                foreach (UserEntity user in users)
                {
                    foreach (RoleEntity role in roles)
                    {
                        if (!user.Roles.Any(r => r.RoleId == role.RoleId))
                        {
                            throw new ProviderException(string.Format(ProvidersHelper.User_Already_Not_In_Role, user.UserName, role.RoleName));
                        }
                        user.Roles.Remove(role);
                    }
                }
                db.SaveChanges();
            }
        }

        /// <summary>
        /// 判断指定的角色是否存在。
        /// </summary>
        /// <param name="roleName">角色名称。</param>
        /// <returns>指示角色是否存在的布尔值。</returns>
        public override bool RoleExists(string roleName)
        {
            Exception exception = Validation.Parameter<string>("roleName").AssertNotNullOrEmpty().AssertMaxLength(RoleEntity.RoleNameMaxLength).AssertNoCommas().TrimAndValidate(ref roleName);
            if (exception != null)
            {
                throw exception;
            }
            bool result;
            using (DbContext db = CreateDbContext())
            {
                result = db.GetRoles(ApplicationName).Any(r => r.RoleName.ToLower() == roleName.ToLower());
            }
            return result;
        }

        #endregion
    }
}
